package com.logica.tutorial.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.springframework.orm.jpa.support.JpaDaoSupport;

import com.logica.tutorial.model.Address;
import com.logica.tutorial.model.Person;
import com.logica.tutorial.model.Sex;

public class PersonDAOImpl extends JpaDaoSupport implements PersonDAO {

	@SuppressWarnings("unchecked")
	public Collection<Person> findPersonsByFirstName(String firstName) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("firstName", firstName);

		return getJpaTemplate().findByNamedParams(
				"from Person as p where p.firstName = :firstName", params);
	}

	@SuppressWarnings("unchecked")
	public Collection<Person> findPersonsInCity(String city) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("city", city);

		return getJpaTemplate().findByNamedParams(
				"from Person as p where p.address.city = :city", params);
	}

	public void changeNameByID(Long idOfPersonToChange, String newFirstName,
			String newLastName) {
		Person person = getJpaTemplate().find(Person.class, idOfPersonToChange);
		person.setFirstName(newFirstName);
		person.setLastName(newLastName);
	}

	public long getNrOfPersons() {
		return ((Long) getJpaTemplate().find("select count(*) from Person")
				.get(0)).longValue();
	}

	@SuppressWarnings("unchecked")
	public Collection<Person> findAllPersonsOfSex(Sex sex) {
		Map<String, Sex> params = new HashMap<String, Sex>();
		params.put("sex", sex);

		return getJpaTemplate().findByNamedParams(
				"from Person as p where p.sex = :sex", params);
	}

	@SuppressWarnings("unchecked")
	public Collection<Person> findAllPersonsWithoutChildren() {
		return getJpaTemplate()
				.find("select p from Person as p where not exists(select c from Person as c where c.father = p.personId or c.mother = p.personId)");
	}

	@SuppressWarnings("unchecked")
	public Collection<Person> findPersonsWhereLastNameStartsWith(char character) {
		return getJpaTemplate().find(
				"select p from Person as p where p.lastName like '"
						+ Character.toUpperCase(character) + "%'");
	}

	public void removePerson(Person person) {
		Person personToRemove = getPersonById(person.getId());

		if (personToRemove.getFather() != null)
			person.getFather().getChildren().remove(personToRemove);
		if (personToRemove.getMother() != null)
			personToRemove.getMother().getChildren().remove(personToRemove);
		Collection<Person> children = personToRemove.getChildren();
		Iterator<Person> it = children.iterator();

		while (it.hasNext()) {
			if (personToRemove.getSex() == Sex.MALE)
				it.next().setFather(null);
			else
				it.next().setMother(null);
		}

		personToRemove.getFriends().clear();
		personToRemove.getFriendOf().clear();

		getJpaTemplate().remove(personToRemove);
	}

	public void addPerson(Person person) {
		System.out.println("Address ID : " + person.getAddress().getId());
		getJpaTemplate().persist(person);
		getJpaTemplate().flush();
	}

	@SuppressWarnings("unchecked")
	public boolean isIdInUse(Long personId) {
		Map<String, Long> params = new HashMap<String, Long>();
		params.put("personId", personId);
		Collection<Long> answer = getJpaTemplate()
				.findByNamedParams(
						"select p.personId from Person as p where p.personId = :personId",
						params);

		if (answer.size() > 0)
			return true;
		else
			return false;
	}

	@SuppressWarnings("unchecked")
	public Collection<Person> findAllPersons() {
		return getJpaTemplate().find("select p from Person as p");
	}

	public Collection<Person> findPerson(Person personToFind) {
		CriteriaBuilder builder = getJpaTemplate().getEntityManager()
				.getCriteriaBuilder();
		CriteriaQuery<Person> query = builder.createQuery(Person.class);
		Root<Person> person = query.from(Person.class);
		query.select(person);

		List<Predicate> predicateList = new ArrayList<Predicate>();
		Predicate idPredicate, firstNamePredicate, lastNamePredicate, sexPredicate, cityPredicate, streetPredicate, houseNrPredicate, zipCodePredicate;

		if (personToFind.getId() != null) {
			idPredicate = builder.equal(person.<Long> get("personId"),
					personToFind.getId());
			predicateList.add(idPredicate);
		}

		if ((personToFind.getFirstName() != null)
				&& (!(personToFind.getFirstName().isEmpty()))) {
			firstNamePredicate = builder.like(
					builder.upper(person.<String> get("firstName")), "%"
							+ personToFind.getFirstName().toUpperCase() + "%");
			predicateList.add(firstNamePredicate);
		}

		if ((personToFind.getLastName() != null)
				&& (!(personToFind.getLastName().isEmpty()))) {
			lastNamePredicate = builder.like(
					builder.upper(person.<String> get("lastName")), "%"
							+ personToFind.getLastName().toUpperCase() + "%");
			predicateList.add(lastNamePredicate);
		}

		if ((personToFind.getSex() != null)
				&& (personToFind.getSex() != Sex.NA)) {
			sexPredicate = builder.equal(person.<Sex> get("sex"),
					personToFind.getSex());
			predicateList.add(sexPredicate);
		}

		if ((personToFind.getAddress().getCity() != null)
				&& (!personToFind.getAddress().getCity().isEmpty())) {
			cityPredicate = builder.like(
					builder.upper(person.<Address> get("address").<String> get(
							"city")), "%"
							+ personToFind.getAddress().getCity().toUpperCase()
							+ "%");
			predicateList.add(cityPredicate);
		}

		if ((personToFind.getAddress().getStreet() != null)
				&& (!personToFind.getAddress().getStreet().isEmpty())) {
			streetPredicate = builder.like(
					builder.upper(person.<Address> get("address").<String> get(
							"street")), "%"
							+ personToFind.getAddress().getStreet()
									.toUpperCase() + "%");
			predicateList.add(streetPredicate);
		}

		if ((personToFind.getAddress().getHouseNr() != null)
				&& (!personToFind.getAddress().getHouseNr().isEmpty())) {
			houseNrPredicate = builder.like(
					builder.upper(person.<Address> get("address").<String> get(
							"houseNr")), "%"
							+ personToFind.getAddress().getHouseNr()
									.toUpperCase() + "%");
			predicateList.add(houseNrPredicate);
		}

		if (personToFind.getAddress().getZipCode() != null) {
			zipCodePredicate = builder.equal(person.<Address> get("address").<Integer> get("zipCode"),
					personToFind.getAddress().getZipCode());
			predicateList.add(zipCodePredicate);
		}
		
		Predicate[] predicates = new Predicate[predicateList.size()];
		predicateList.toArray(predicates);
		query.where(predicates);

		return getJpaTemplate().getEntityManager().createQuery(query)
				.getResultList();
	}

	@Override
	public Person getPersonById(Long id) {
		return getJpaTemplate().find(Person.class, id);
	}
}
